#include "Shader.h"
#include "Texture2D.h"
GLuint Shader::CompileShader(GLenum shaderType, const char *url)
{
	char *shaderCode = ReadAllBytes(url);
	if (!shaderCode)
		return 0;

	char *shaderTypeStr = "Vertex Shader";
	if (shaderType == GL_FRAGMENT_SHADER)
		shaderTypeStr = "Fragment Shader";

	GLuint shader = glCreateShader(shaderType);
	if (!shader)
		throw;
	glShaderSource(shader, 1, &shaderCode, NULL);
	glCompileShader(shader);
	GLint success = GL_TRUE;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		char infolog[1024];
		GLsizei logLen = 0;
		glGetShaderInfoLog(shader, sizeof(infolog), &logLen, infolog);
		glDeleteShader(shader);
		throw;
		return 0;
	}
	delete shaderCode;
	return shader;
}


GLuint Shader::CreateShaderProgram(const char* vs, const char *fs)
{
	GLuint vsShader = CompileShader(GL_VERTEX_SHADER, vs);
	if (vsShader <= 0)
		return 0;
	GLuint fsShader = CompileShader(GL_FRAGMENT_SHADER, fs);
	if (fsShader <= 0)
		return 0;

	GLuint shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vsShader);
	glAttachShader(shaderProgram, fsShader);
	glLinkProgram(shaderProgram);

	GLint success = GL_TRUE;
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success)
	{
		char infolog[1024];
		GLsizei logLen = 0;
		glGetShaderInfoLog(shaderProgram, sizeof(infolog), &logLen, infolog);
		glDeleteProgram(shaderProgram);
		throw;
		return 0;
	}
	glDetachShader(shaderProgram, vsShader);
	glDetachShader(shaderProgram, fsShader);
	glDeleteShader(vsShader);
	glDeleteShader(fsShader);
	return shaderProgram;
}

Shader::Shader(const char* vsUrl, const char* fsUrl)
{

	if ((program = CreateShaderProgram(vsUrl, fsUrl)) <= 0)
		throw;
	if ((std_position_location = glGetAttribLocation(program, "std_position")) < 0)
		throw;
	std_normal_location = glGetAttribLocation(program, "std_normal");
	std_texcoord_location = glGetAttribLocation(program, "std_texcoord");
	std_model_mat_location = glGetUniformLocation(program, "std_modelMat");
	std_view_mat_location = glGetUniformLocation(program, "std_viewMat");
	std_proj_mat_location = glGetUniformLocation(program, "std_projMat");

}

Shader::~Shader()
{
	glDeleteProgram(program);
}

void Shader::Apply()
{
	memset(textureUnit, 0, sizeof(textureUnit));
	glUseProgram(program);
}

void Shader::SetTexture2D(const char* locationName, Texture2D *texture)
{
	int unit = -1;
	for (int i = 0; i < MAX_TEXTURE_UNIT; i++)
	{
		if (!textureUnit[i])
		{
			unit = i;
			textureUnit[unit] = true;
			break;
		}
	}
	if (unit < 0)
		throw;

	GLenum textureID = GL_TEXTURE0 + unit;
	glActiveTexture(textureID);
	glBindTexture(GL_TEXTURE_2D, texture->texID);
	GLint location = glGetUniformLocation(program, locationName);
	if (location < 0)
		throw;
	glUniform1i(location, unit);
}
